#include "bsp_dart.h"
#include "custom.h"

const char TAG_POWER[] = "Power";
// static StaticSemaphore_t xMutexBuffer;

esp_err_t __readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t nBytesRead)
{
    // sem
    if (xSemaphoreTake(__i2c_mutex, portMAX_DELAY) != pdTRUE)
    {
        ESP_LOGE(TAG_POWER, "Could not take i2c mutex");
        return ESP_FAIL;
    }
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, address << 1 | WRITE_BIT, ACK_CHECK_EN);
    i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, address << 1 | READ_BIT, ACK_CHECK_EN);
    if (nBytesRead > 1)
        i2c_master_read(cmd, data, nBytesRead - 1, ACK_VAL);
    i2c_master_read_byte(cmd, data + nBytesRead - 1, NACK_VAL);
    i2c_master_stop(cmd);

    int ret = i2c_master_cmd_begin((i2c_port_t)CONFIG_BSP_I2C_NUM, cmd, CONFIG_I2C_TIMEOUT_MS / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);

    // i2c_master_write_read_device((i2c_port_t)CONFIG_BSP_I2C_NUM, address, &reg, 1, data, 1, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
    ESP_LOGD(TAG_POWER, "Read from register 0x%02x: 0x%02x", reg, *data);
    // char info[40];
    // snprintf(info, sizeof(info), "Read from register 0x%02x: 0x%02x", reg, *data);
    // _send_msg("提示", info, MSG_EMERG_INFO);
    xSemaphoreGive(__i2c_mutex);
    return ret;
}

esp_err_t __writeRegister(uint8_t address, uint8_t reg, uint8_t data)
{
    // sem
    if (xSemaphoreTake(__i2c_mutex, portMAX_DELAY) != pdTRUE)
    {
        ESP_LOGE(TAG_POWER, "Could not take i2c mutex");
        return ESP_FAIL;
    }
    i2c_cmd_handle_t cmd = i2c_cmd_link_create();

    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (address << 1) | WRITE_BIT, ACK_CHECK_EN);
    i2c_master_write_byte(cmd, reg, ACK_CHECK_EN);

    i2c_master_write_byte(cmd, data, ACK_CHECK_EN);

    i2c_master_stop(cmd);

    esp_err_t ret = i2c_master_cmd_begin((i2c_port_t)CONFIG_BSP_I2C_NUM, cmd, CONFIG_I2C_TIMEOUT_MS / portTICK_PERIOD_MS);
    i2c_cmd_link_delete(cmd);

    // static uint8_t dataCheck;
    // __readRegister(address, reg, &dataCheck);
    // if (dataCheck != data)
    //     return ESP_FAIL;

    xSemaphoreGive(__i2c_mutex);
    return ret;
}

void _daemonHUSB238(void *args)
{
    // Update HUSB238 Status and update voltage and watts to lvgl label using om_topic
    uint8_t voltage;
    uint16_t current;
    while (1)
    {
        bsp_display_lock(portMAX_DELAY);
        update_pd_status();
        voltage = m_src_voltage[status.PD_SRC_VOLTAGE];
        current = m_src_current[status.PD_SRC_CURRENT];

        lv_label_set_text_fmt(guider_ui.Main_labelPDInfo, "%dV/%dW", voltage, (current * voltage) / 1000);
        bsp_display_unlock();
        vTaskDelay(1000);
    }
    vTaskDelete(NULL);
}

void initHUSB238()
{
    // Semaphore Create Static
    // __i2c_mutex = xSemaphoreCreateMutexStatic(&xMutexBuffer);
    if (get_capabilities() != 0)
        ESP_LOGE(TAG_POWER, "No PD Capabilities found.");
    else
        ESP_LOGI(TAG_POWER, "PD Controller Init");
}

void initHUSB238Daemon()
{
    // Create Daemon
    xTaskCreate(_daemonHUSB238, "daemonHUSB238", 4096, NULL, 5, NULL);
}

uint64_t get_capabilities()
{
    uint64_t return_codes = 0;

    for (int i = 0; i < m_capability_reg_count; i++)
    {
        // As the capability struct is packed together, we can iterate over them.
        uint16_t *capability_address = &capability.ma_5V + i;
        // As the pdo registers are consecutive, we can iterate through them
        uint8_t pdo_register = REG_SRC_PDO_5V + i;

        uint8_t response;
        if (__readRegister(m_i2c_address, pdo_register, &response) != ESP_OK)
            return_codes |= (0 << i * 8);
        else
            return_codes |= (1 << i * 8);

        if (!(response & 0xF0))
        {
            // PSU doesn't support this voltage
            *capability_address = 0;
        }
        else
        {
            // Voltage is supported at the given current
            uint8_t current_code = response & 0x0F;
            *capability_address = m_src_current[current_code];
        }
    }
    return return_codes;
}

void set_voltage(voltage_select voltage)
{
    __writeRegister(m_i2c_address, REG_SRC_PDO, voltage);
    __writeRegister(m_i2c_address, REG_GO_COMMAND, 1);
}

esp_err_t update_pd_status()
{
    // m_wire->beginTransmission(m_i2c_address);
    // status.PD_STATUS0 = m_read_byte_from_register(REG_PD_STATUS0);
    // status.PD_STATUS1 = m_read_byte_from_register(REG_PD_STATUS0);
    // return m_wire->endTransmission();
    return __readRegister(m_i2c_address, REG_PD_STATUS0, &status.PD_STATUS0);
}

uint8_t get_pd_voltage()
{
    if (update_pd_status() != ESP_OK)
        return 0;
    return m_src_voltage[status.PD_SRC_VOLTAGE];
}

uint16_t get_pd_current()
{
    update_pd_status();

    if (status.VOLTAGE_5V)
    {
        return m_apple_current[status.CURRENT_5V];
    }
    else
    {
        return m_src_current[status.PD_SRC_CURRENT];
    }
}

void setFanSpeedPercentage(uint8_t percentage)
{
    if (percentage > 100)
        percentage = 100;
    ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, percentage * 2.55);
    ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
}

void initFan()
{
    // Init ledc
    ledc_timer_config_t ledc_timer = {
        .speed_mode = LEDC_LOW_SPEED_MODE,   // timer mode
        .duty_resolution = LEDC_TIMER_8_BIT, // resolution of PWM duty
        .timer_num = LEDC_TIMER_0,           // timer index
        .freq_hz = 5000,                     // frequency of PWM signal
        .clk_cfg = LEDC_AUTO_CLK,            // Auto select the source clock
    };
    ledc_timer_config(&ledc_timer);

    // Channel
    // GPIO bind to IO16
    ledc_channel_config_t ledc_channel = {
        .gpio_num = 16,
        .speed_mode = LEDC_LOW_SPEED_MODE,
        .channel = LEDC_CHANNEL_0,
        .timer_sel = LEDC_TIMER_0,
        .duty = 0,
        .hpoint = 0,
    };
    ledc_channel_config(&ledc_channel);

    // Set Fan Speed to 0
    setFanSpeedPercentage(0);
}